home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 076-100 / disk_095 / journal / journal.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  21KB  |  691 lines

  1. /*
  2.  *  JOURNAL.C  -  Records all mouse and keyboard activity so that
  3.  *                it can be played back for demonstration of products,
  4.  *                reporting errors, etc.
  5.  *
  6.  *             Copyright (c) 1987 by Davide P. Cervone
  7.  *  You may use this code provided this copyright notice is kept intact.
  8.  */
  9.  
  10. #include "journal.h"
  11.  
  12. /*
  13.  *  Version number and author:
  14.  */
  15. char version[32] = "Journal v1.0 (June 1987)";
  16. char *author     = "Copyright (c) 1987 by Davide P. Cervone";
  17.  
  18.  
  19. /*
  20.  *  Macros used to check for end-of-journal
  21.  */
  22. #define CTRL_AMIGA      (IEQUALIFIER_CONTROL | IEQUALIFIER_LCOMMAND)
  23. #define KEY_E           0x12
  24. #define CTRL_AMIGA_E(e) ((((e)->ie_Qualifier & CTRL_AMIGA) == CTRL_AMIGA) &&\
  25.                           ((e)->ie_Code == KEY_E))
  26.  
  27. /*
  28.  *  Match a command-line argument against a string (case insensitive)
  29.  */
  30. #define ARGMATCH(s)   (stricmp(s,*Argv) == 0)
  31.  
  32. /*
  33.  *  Functions that JOURNAL can perform
  34.  */
  35. #define SHOW_USAGE      0
  36. #define WRITE_JOURNAL   1
  37. #define JUST_EXIT       2
  38.  
  39. /*
  40.  *  Largest mouse move we want to record
  41.  */
  42. #define MAXMOUSEMOVE   32
  43.  
  44. /*
  45.  *  Macros to tell whether a mouse movement event can be compressed with
  46.  *  other mouse movement events
  47.  */
  48. #define MOUSEMOVE(e)\
  49.    ((e)->ie_Class == IECLASS_RAWMOUSE && (e)->ie_Code == IECODE_NOBUTTON &&\
  50.    ((e)->ie_Qualifier & IEQUALIFIER_RELATIVEMOUSE))
  51. #define BIGX(e)         ((e)->ie_X >= XMINMOUSE || (e)->ie_X <= -XMINMOUSE)
  52. #define BIGY(e)         ((e)->ie_Y >= YMINMOUSE || (e)->ie_Y <= -YMINMOUSE)
  53. #define BIGTICKS(e)     ((e)->my_Ticks > LONGTIME)
  54. #define NOTSAVED(e)     ((e)->my_Saved == FALSE)
  55. #define SETSAVED(e)     ((e)->my_Saved = TRUE)
  56.  
  57.  
  58. /*
  59.  *  Global Variables:
  60.  */
  61.  
  62. struct MsgPort *InputPort = NULL;     /* Port used to talk to Input.Device */
  63. struct IOStdReq *InputBlock = NULL;   /* request block used with Input.Device */
  64. struct Task *theTask = NULL;          /* pointer to our task */
  65. LONG InputDevice = 0;                 /* flag whether Input.Device is open */
  66. LONG theSignal = 0;                   /* signal used when an event is ready */
  67. LONG ErrSignal = 0;                   /* signal used when an error occured */
  68. LONG theMask;                         /* 1 << theSignal */
  69. LONG ErrMask;                         /* 1 << ErrSignal */
  70.  
  71. UWORD Ticks = 0;                      /* number of timer ticks between events */
  72. LONG  TimerMics = 0;                  /* last timer event's micros field */
  73.  
  74. WORD xmove = XDEFMIN;                 /* distance to compress into one event */
  75. WORD ymove = YDEFMIN;                 /* distance to compress into one event */
  76. WORD smoothxmove = 1;                 /* distance for smoothed events */
  77. WORD smoothymove = 1;                 /* distnace for smoothed events */
  78. WORD xminmove, yminmove;              /* distance actually in use */
  79. UWORD SmoothMask = IEQUALIFIER_LCOMMAND;
  80.                                       /* what keys are required for smoothing */
  81. UWORD SmoothTrigger = 0xFFFF;         /* any of these keys trigger smoothing */
  82.  
  83. int Action = WRITE_JOURNAL;           /* action to be perfomed by JOURNAL */
  84. int ArgMatched = FALSE;               /* TRUE if a parameter matched OK */
  85. int Argc;                             /* global version of argc */
  86. char **Argv;                          /* global version of argv */
  87.  
  88. struct InputEvent **EventPtr = NULL;  /* pointer to (pointer to next event) */
  89. struct InputEvent *OldEvent = NULL;   /* pointer to last event */
  90. struct SmallEvent TinyEvent;          /* packed event (ready to record) */
  91.  
  92. FILE *OutFile = NULL;                 /* where the events will be written */
  93. char *JournalFile = NULL;             /* name of the output file */
  94.  
  95. int NotDone = TRUE;                   /* continue looking for events? */
  96. int NotFirstEvent = FALSE;            /* TRUE after an event was recorded */
  97. int PointerNotHomed = TRUE;           /* TRUE until pointer is moved */
  98.  
  99. struct Interrupt HandlerData =        /* used to add an input handler */
  100. {
  101.    {NULL, NULL, 0, 51, NULL},           /* Node structure (nl_Pri = 51) */
  102.    NULL,                                /* data pointer */
  103.    &myHandlerStub                       /* code pointer */
  104. };
  105.  
  106. struct InputEvent PointerToHome =     /* event to put pointer in upper-left */
  107. {
  108.    NULL,                                /* pointer to next event */
  109.    IECLASS_RAWMOUSE,                    /* ie_Class = RAWMOUSE */
  110.    0,                                   /* ie_SubClass */
  111.    IECODE_NOBUTTON,                     /* ie_Code = NOBUTTON (just a move) */
  112.    IEQUALIFIER_RELATIVEMOUSE,           /* ie_Qualifier = relative move */
  113.    {-1000,-1000},                       /* move far to left and top */
  114.    {0L,0L}                              /* seconds and micros */
  115. };
  116.  
  117. struct SmallEvent TimeEvent =         /* pause written at beginning of file */
  118. {
  119.    {0xE2, 0, 0},                        /* MOUSEMOVE, NOBUTTON, X=0, Y=0 */
  120.    IEQUALIFIER_RELATIVEMOUSE,           /* Qualifier */
  121.    0x00A00000                           /* 1 second pause */
  122. };
  123.  
  124.  
  125. /*
  126.  *  myHandler()
  127.  *
  128.  *  This is the input handler that makes copies of the input events and sends 
  129.  *  them the to main process to be written to the output file.
  130.  *
  131.  *  The first time around, we add the PointerToHome event into the stream
  132.  *  so that the pointer is put into a known position.
  133.  *
  134.  *  We check the event type of each event in the list, and do the following:
  135.  *  for Timer events, we increment the tick count (which tells how many ticks
  136.  *  have occured since the last recorded event); for raw key events, we check
  137.  *  whether a CTRL-AMIGA-E has been pressed (if so, we signal the main process
  138.  *  with a CTRL-E which tells it to remove the handler and quit); for raw 
  139.  *  mouse and raw key events, we allocate memory for a new copy of the event
  140.  *  (and signal an error if we can't), and copy the pertinent information
  141.  *  from the current event into the copy event and mark it as not-yet-saved.
  142.  *  We link it into the copied-event list (via EventPtr), and signal the
  143.  *  main task that a new event is ready, and then zero the tick count.
  144.  *
  145.  *  Any other type of event is ignored.
  146.  *
  147.  *  When we are through with the event list, we return it so that Intuition
  148.  *  can use it to do its thing.
  149.  */
  150.  
  151. struct InputEvent *myHandler(event,data)
  152. struct InputEvent *event;
  153. APTR data;
  154. {
  155.    struct InputEvent *theEvent = event;
  156.    struct InputEvent *theCopy;
  157.  
  158.    Forbid();
  159.    if (PointerNotHomed)
  160.    {
  161.       PointerToHome.ie_NextEvent = event;
  162.       event = &PointerToHome;
  163.       PointerNotHomed = FALSE;
  164.    }
  165.    while(theEvent)
  166.    {
  167.       switch(theEvent->ie_Class)
  168.       {
  169.          case IECLASS_TIMER:
  170.             Ticks++;
  171.             TimerMics = theEvent->ie_Mics;
  172.             break;
  173.  
  174.          case IECLASS_RAWKEY:
  175.             if (CTRL_AMIGA_E(theEvent)) Signal(theTask,SIGBREAKF_CTRL_E);
  176.  
  177.          case IECLASS_RAWMOUSE:
  178.             theCopy = NEWEVENT;
  179.             if (theCopy == NULL)
  180.             {
  181.                Signal(theTask,ErrMask);
  182.             } else {
  183.                theCopy->ie_NextEvent    = NULL;
  184.                theCopy->ie_Class        = theEvent->ie_Class;
  185.                theCopy->ie_Code         = theEvent->ie_Code;
  186.                theCopy->ie_Qualifier    = theEvent->ie_Qualifier;
  187.                theCopy->ie_EventAddress = theEvent->ie_EventAddress;
  188.                theCopy->my_Time         = TIME;
  189.                theCopy->my_Ticks        = Ticks;
  190.                theCopy->my_Saved        = FALSE;
  191.                *EventPtr = theCopy;
  192.                EventPtr = &(theCopy->ie_NextEvent);
  193.                Signal(theTask,theMask);
  194.                Ticks = 0;
  195.             }
  196.             break;
  197.       }
  198.       theEvent = theEvent->ie_NextEvent;
  199.    }
  200.    Permit();
  201.    return(event);
  202. }
  203.  
  204.  
  205. /*
  206.  *  Ctrl_C()
  207.  *
  208.  *  Dummy routine to disable Lattice-C CTRL-C trapping.
  209.  */
  210.  
  211. #ifndef MANX
  212. int Ctrl_C()
  213. {
  214.    return(0);
  215. }
  216. #endif
  217.  
  218.  
  219. /*
  220.  *  DoExit()
  221.  *
  222.  *  General purpose exit routine.  If 's' is not NULL, then print an
  223.  *  error message with up to three parameters.  Free any memory, close
  224.  *  any open files, delete any ports, free any used signals, etc.
  225.  */
  226.  
  227. void DoExit(s,x1,x2,x3)
  228. char *s, *x1, *x2, *x3;
  229. {
  230.    long status = 0;
  231.    
  232.    if (s != NULL)
  233.    {
  234.       printf(s,x1,x2,x3);
  235.       printf("\n");
  236.       status = RETURN_ERROR;
  237.    }
  238.    if (OldEvent)    FREEVENT(OldEvent);
  239.    if (OutFile)     fclose(OutFile);
  240.    if (InputDevice) CloseDevice(InputBlock);
  241.    if (InputBlock)  DeleteStdIO(InputBlock);
  242.    if (InputPort)   DeletePort(InputPort);
  243.    if (theSignal)   FreeSignal(theSignal);
  244.    if (ErrSignal)   FreeSignal(ErrSignal);
  245.    exit(status);
  246. }
  247.  
  248.  
  249. /*
  250.  *  CheckNumber()
  251.  *
  252.  *  Check a command-line argument for the given keyword, and if it matches,
  253.  *  makes sure that the next parameter is a positive numeric value that
  254.  *  is less than the maximum expected value.
  255.  */
  256.  
  257. void CheckNumber(keyword,value)
  258. char *keyword;
  259. WORD *value;
  260. {
  261.    long lvalue = *value;
  262.  
  263.    if (Argc > 1 && ARGMATCH(keyword))
  264.    {
  265.       ArgMatched = TRUE;
  266.       Argc--;
  267.       if (sscanf(*(++Argv),"%ld",&lvalue) != 1)
  268.       {
  269.          printf("%s must be numeric:  '%s'\n",keyword,*Argv);
  270.          Action = JUST_EXIT;
  271.       }
  272.       if (lvalue < 1 || lvalue > MAXMOUSEMOVE)
  273.       {
  274.          printf("%s must be positive and less than %d:  '%ld'\n",
  275.             keyword,MAXMOUSEMOVE,lvalue);
  276.          Action = JUST_EXIT;
  277.       }
  278.       *value = lvalue;
  279.    }
  280. }
  281.  
  282.  
  283. /*
  284.  *  CheckHexNum()
  285.  *
  286.  *  Check a command-line argument for the given keyword, and if it
  287.  *  matches, make sure that the next parameter is a legal HEX value.
  288.  */
  289.  
  290. void CheckHexNum(keyword,value)
  291. char *keyword;
  292. WORD *value;
  293. {
  294.    ULONG lvalue = *value;
  295.  
  296.    if (Argc > 1 && ARGMATCH(keyword))
  297.    {
  298.       ArgMatched = TRUE;
  299.       Argc--;
  300.       if (sscanf(*(++Argv),"%lx",&lvalue) != 1)
  301.       {
  302.          printf("%s must be a HEX number:  '%s'\n",keyword,*Argv);
  303.          Action = JUST_EXIT;
  304.       }
  305.       *value = lvalue;
  306.    }
  307. }
  308.  
  309.  
  310. /*
  311.  *  ParseArguements()
  312.  *
  313.  *  Check that all the command-line arguments are valid and set the 
  314.  *  proper variables as requested by the user.  If no keyword is specified,
  315.  *  assume "TO".  If no file is specified, then show the usage.  DX and DY
  316.  *  set the "granularity" of the mouse moves recorded (moves are combined
  317.  *  into a single event until it's movement excedes either DX or DY).
  318.  *  Similarly, SMOOTHX and SMOOTHY set alternate DX and DY values
  319.  *  for when extra precision is needed (i.e., when drawing curves in a paint
  320.  *  program).  SMOOTH specifies what qualifier keys MUST be present to 
  321.  *  activate the SMOOTHX and SMOOTHY values, and TRIGGER specifies a set of
  322.  *  qualifiers any one of which (together with the SMOOTH qualifiers)
  323.  *  will active the SMOOTHX and SMOOTHY values.  In other words, all the
  324.  *  SMOOTH qualifiers plus at least one of the TRIGGER qualifiers must be
  325.  *  pressed in order to activate the smooth values.  For example, if SMOOTH
  326.  *  is 0 and TRIGGER is 0x6000, then holding down either the left or the
  327.  *  right button will activate the smooth values.  If SMOOTH is 0x0040 rather
  328.  *  than 0, then the left Amiga button must also be held down in order to 
  329.  *  activate SMOOTHX and SMOOTHY.  The qualifier flags are listed in
  330.  *  DEVICES/INPUTEVENT.H
  331.  *
  332.  *  The default values are DX = 8, DY = 8, SMOOTHX = 1, SMOOTHY = 1,
  333.  *  SMOOTH = left Amiga, TRIGGER = 0xFFFF.
  334.  */
  335.  
  336. int ParseArguments(argc,argv)
  337. int argc;
  338. char **argv;
  339. {
  340.    Argc = argc;
  341.    Argv = argv;
  342.  
  343.    while (--Argc > 0)
  344.    {
  345.       ArgMatched = FALSE;
  346.       Argv++;
  347.       if (Argc > 1 && ARGMATCH("TO"))
  348.       {
  349.          JournalFile = *(++Argv);
  350.          Argc--;
  351.          ArgMatched = TRUE;
  352.       }
  353.       CheckNumber("DX",&xmove);
  354.       CheckNumber("DY",&ymove);
  355.       CheckNumber("SMOOTHX",&smoothxmove);
  356.       CheckNumber("SMOOTHY",&smoothymove);
  357.       CheckHexNum("SMOOTH",&SmoothMask);
  358.       CheckHexNum("TRIGGER",&SmoothTrigger);
  359.       if (ArgMatched == FALSE)
  360.       {
  361.          if (JournalFile == NULL)
  362.             JournalFile = *Argv;
  363.            else
  364.             Action = SHOW_USAGE;
  365.       }
  366.    }
  367.    if (JournalFile == NULL && Action == WRITE_JOURNAL) Action = SHOW_USAGE;
  368.    return(Action);
  369. }
  370.  
  371.  
  372. /*
  373.  *  OpenJournal()
  374.  *
  375.  *  Open the journal file and check for errors.  Write the version
  376.  *  information to the file.
  377.  */
  378.  
  379. void OpenJournal()
  380. {
  381.    OutFile = fopen(JournalFile,"w");
  382.    if (OutFile == NULL)
  383.       DoExit("Can't Open Journal File '%s', error %ld",JournalFile,_OSERR);
  384.    if (fwrite(version,sizeof(version),1,OutFile) != 1)
  385.       DoExit("Error writing to output file:  %ld",_OSERR);
  386. }
  387.  
  388.  
  389. /*
  390.  *  GetSignal()
  391.  *
  392.  *  Allocate a signal (error if none available) and set the mask to
  393.  *  the proper value.
  394.  */
  395.  
  396. void GetSignal(theSignal,theMask)
  397. LONG *theSignal, *theMask;
  398. {
  399.    LONG signal;
  400.  
  401.    if ((signal = AllocSignal(-ONE)) == -ONE) DoExit("Can't Get Signal");
  402.    *theSignal = signal;
  403.    *theMask = (ONE << signal);
  404. }
  405.  
  406.  
  407. /*
  408.  *  SetupTask()
  409.  *
  410.  *  Find the task pointer for the main task (so the input handler can 
  411.  *  signal it).  Clear the CTRL signal flags (so we don't get any left
  412.  *  over from before JOURNAL was run) and allocate some signals for 
  413.  *  new events and errors (so the input handler can signal them).
  414.  */
  415.  
  416. void SetupTask()
  417. {
  418.    theTask = FindTask(NULL);
  419.    SetSignal(0L,SIGBREAKF_ANY);
  420.    GetSignal(&theSignal,&theMask);
  421.    GetSignal(&ErrSignal,&ErrMask);
  422.    #ifndef MANX
  423.       onbreak(&Ctrl_C);
  424.    #endif
  425. }
  426.  
  427.  
  428. /*
  429.  *  SetupEvents()
  430.  *
  431.  *  Get a fake old-event to start off with, and mark it as saved (so we don't
  432.  *  really try to use it).  Make it the end of the list (set its next pointer
  433.  *  to NULL.  Tell the input handler where to start allocating new events
  434.  *  by setting EventPtr to point the the next-pointer.  When the input
  435.  *  handler allocates a new copy of an event, it will link it to this one
  436.  *  so the main process can find it by following the next-pointer from the
  437.  *  old event.
  438.  */
  439.  
  440. void SetupEvents()
  441. {
  442.    if ((OldEvent = NEWEVENT) == NULL) DoExit("No Memory for OldEvent");
  443.    SETSAVED(OldEvent);
  444.    OldEvent->ie_NextEvent = NULL;
  445.    EventPtr = &(OldEvent->ie_NextEvent);
  446. }
  447.  
  448.  
  449. /*
  450.  *  AddHandler()
  451.  *
  452.  *  Add the input handler to the input.device handler chain.  Since the
  453.  *  priority is 51, it will appear BEFORE intuition, so all it should
  454.  *  see are raw key, raw mouse, timer, and disk insert/remove events.
  455.  */
  456.  
  457. void AddHandler()
  458. {
  459.    long status;
  460.  
  461.    if ((InputPort = CreatePort(0,0)) == NULL)
  462.       DoExit("Can't Create Port");
  463.    if ((InputBlock = CreateStdIO(InputPort)) == NULL)
  464.       DoExit("Can't Create Standard IO Block");
  465.    InputDevice = (OpenDevice("input.device",0,InputBlock,0) == 0);
  466.    if (InputDevice == 0) DoExit("Can't Open Input Device");
  467.    
  468.    InputBlock->io_Command = IND_ADDHANDLER;
  469.    InputBlock->io_Data    = (APTR) &HandlerData;
  470.    if (status = DoIO(InputBlock)) DoExit("Error from DoIO:  %ld",status);
  471.    printf("%s - Press CTRL-AMIGA-E to End Journal\n",version);
  472. }
  473.  
  474.  
  475. /*
  476.  *  RemoveHandler()
  477.  *
  478.  *  Remove the input handler from the input.device handler chain.
  479.  */
  480.  
  481. void RemoveHandler()
  482. {
  483.    long status;
  484.  
  485.    if (InputDevice && InputBlock)
  486.    {
  487.       InputBlock->io_Command = IND_REMHANDLER;
  488.       InputBlock->io_Data = (APTR) &HandlerData;
  489.       if (status = DoIO(InputBlock)) DoExit("Error from DoIO:  %ld",status);
  490.    }
  491.    printf("Journal Complete\n");
  492. }
  493.  
  494.  
  495. /*
  496.  *  SaveEvent()
  497.  *
  498.  *  Pack an InputEvent into a SmallEvent (by shifting bits around) so that
  499.  *  it takes up less space in the output file.  Write the SmallEvent to the
  500.  *  output file, and mark it as already-saved.
  501.  */
  502.  
  503. void SaveEvent(theEvent)
  504. struct InputEvent *theEvent;
  505. {
  506.    if (theEvent->my_Time > MILLION) theEvent->my_Time += MILLION;
  507.    TinyEvent.se_XY        = 0;
  508.    TinyEvent.se_Type      = theEvent->ie_Class;
  509.    TinyEvent.se_Qualifier = theEvent->ie_Qualifier;
  510.    TinyEvent.se_Long2     = (theEvent->my_Ticks << 20) |
  511.                             (theEvent->my_Time & 0xFFFFF);
  512.  
  513.    if (theEvent->ie_Class == IECLASS_RAWKEY)
  514.    {
  515.       TinyEvent.se_Code  = theEvent->ie_Code;
  516.       TinyEvent.se_Prev  = theEvent->my_Prev;
  517.    } else {
  518.       TinyEvent.se_Type |= (theEvent->ie_Code & IECODE_UP_PREFIX) |
  519.                            ((theEvent->ie_Code & 0x03) << 5);
  520.       TinyEvent.se_XY |= (theEvent->ie_X & 0xFFF) |
  521.                          ((theEvent->ie_Y & 0xFFF) << 12);
  522.    }
  523.  
  524.    if (fwrite((char *)&TinyEvent,sizeof(TinyEvent),1,OutFile) != 1)
  525.       DoExit("Error writing to output file:  %ld",_OSERR);
  526.    SETSAVED(theEvent);
  527.    NotFirstEvent = TRUE;
  528. }
  529.  
  530.  
  531. /*
  532.  *  SaveTime()
  533.  *
  534.  *  Save a fake mouse event that doesn't move anywhere but that includes a
  535.  *  tick count.  That is, pause without moving the mouse.
  536.  */
  537.  
  538. void SaveTime(theEvent)
  539. struct InputEvent *theEvent;
  540. {
  541.    if (NotFirstEvent) TimeEvent.se_Ticks = (theEvent->my_Ticks << 20);
  542.    if (fwrite((char *)&TimeEvent,sizeof(TimeEvent),1,OutFile) != 1)
  543.       DoExit("Error writing to output file:  %ld",_OSERR);
  544.    theEvent->my_Ticks = 0;
  545. }
  546.  
  547.  
  548. /*
  549.  *  SaveEventList()
  550.  *
  551.  *  Write the events in the event list (built by the input handler) out
  552.  *  to the output file, compressing multiple, small mouse moves into larger,
  553.  *  single mouse moves (to save space in the output file) in the following 
  554.  *  way:
  555.  *
  556.  *    if the current event is a mouse move (not a button press), then
  557.  *      set its event count to 1 (the number of events compressed into it),
  558.  *      if the user is requesting smooth movement, then use the smooth
  559.  *        movement variables, otherwise use the course (normal) values. 
  560.  *      if the event's x or y movement is big enough, or if there was a long
  561.  *           pause before the movement occured, then
  562.  *        if the old event was not saved, save it.
  563.  *        if the pause was long enough, save a separate pause (so that the
  564.  *           smoothing algorithm in PLAYBACK does not spread the pause over
  565.  *           the entire mouse move).
  566.  *        save the current event.
  567.  *      otherwise, (we can compress the movement)
  568.  *        if there was an old mouse event that was not saved,
  569.  *          add it to the current event,
  570.  *          if the new x or y movement is big enough to record, do so.
  571.  *    otherwise, (this was not a mouse movement)
  572.  *      if there was a previous mouse movement that was not saved, save it.
  573.  *      finally, save the current event.
  574.  *  At this point the OldEvent is either posted, or has been combined with the
  575.  *  current event, so we can free the old event.  The current event then
  576.  *  becomes the old event.
  577.  */
  578.  
  579. void SaveEventList()
  580. {
  581.    struct InputEvent *theEvent;
  582.    
  583.    while ((theEvent = OldEvent->ie_NextEvent) != NULL)
  584.    {
  585.       if (MOUSEMOVE(theEvent))
  586.       {
  587.          theEvent->my_Count &= (~COUNTMASK);
  588.          theEvent->my_Count++;
  589.          if ((theEvent->ie_Qualifier & SmoothMask) == SmoothMask &&
  590.              (theEvent->ie_Qualifier & SmoothTrigger))
  591.          {
  592.             xminmove = smoothxmove;
  593.             yminmove = smoothymove;
  594.          } else {
  595.             xminmove = xmove;
  596.             yminmove = ymove;
  597.          }
  598.          if (BIGX(theEvent) || BIGY(theEvent) || BIGTICKS(theEvent))
  599.          {
  600.             if (NOTSAVED(OldEvent)) SaveEvent(OldEvent);
  601.             if (BIGTICKS(theEvent)) SaveTime(theEvent);
  602.             SaveEvent(theEvent);
  603.          } else {
  604.             if (NOTSAVED(OldEvent))
  605.             {
  606.                theEvent->ie_X += OldEvent->ie_X;
  607.                theEvent->ie_Y += OldEvent->ie_Y;
  608.                theEvent->my_Ticks += OldEvent->my_Ticks;
  609.                theEvent->my_Count += OldEvent->my_Count & COUNTMASK;
  610.                if (BIGX(theEvent) || BIGY(theEvent)) SaveEvent(theEvent);
  611.             }
  612.          }
  613.       } else {
  614.          if (NOTSAVED(OldEvent)) SaveEvent(OldEvent);
  615.          SaveEvent(theEvent);
  616.       }
  617.       FREEVENT(OldEvent);
  618.       OldEvent = theEvent;
  619.    }
  620. }
  621.  
  622.  
  623. /*
  624.  *  RecordJournal()
  625.  *
  626.  *  Open the journal file, set up the task and signals, and set up the
  627.  *  initial pointers for the event list.  Then add the input handler
  628.  *  into the Input.Device handler chain.  
  629.  *
  630.  *  Wait for the input handler to signal us that an event is ready (or that
  631.  *  an error occured), or that the user to press CTRL-AMIGA-E.  If it's the 
  632.  *  latter, cancel the Wait loop, otherwise save the events that are in the
  633.  *  list into the file.  If the error signal was sent, inform the user that
  634.  *  some events were lost.
  635.  *
  636.  *  Once we are signaled to end the journal, remove the handler, and
  637.  *  record any remaining, unsaved events to the file.
  638.  */
  639.  
  640. void RecordJournal()
  641. {
  642.    LONG signals;
  643.    LONG SigMask;
  644.    
  645.    OpenJournal();
  646.    SetupTask();
  647.    SetupEvents();
  648.    SigMask = theMask | ErrMask | SIGBREAKF_CTRL_E;
  649.  
  650.    AddHandler(&myHandler);
  651.    while (NotDone)
  652.    {
  653.       signals = Wait(SigMask);
  654.       if (signals & SIGBREAKF_CTRL_E)
  655.          NotDone = FALSE;
  656.         else
  657.          SaveEventList();
  658.       if (signals & ErrMask)
  659.          printf("[ Out of memory - some events not recorded ]\n");
  660.    }
  661.    RemoveHandler(&myHandler);
  662.    SaveEventList();
  663. }
  664.  
  665.  
  666. /*
  667.  *  main()
  668.  *
  669.  *  Parse the command-line arguments and perform the proper function
  670.  *  (either show the usage, write a journal, or fall through and exit).
  671.  */
  672.  
  673. void main(argc,argv)
  674. int argc;
  675. char **argv;
  676. {
  677.    switch(ParseArguments(argc,argv))
  678.    {
  679.       case SHOW_USAGE:
  680.          printf("Usage:  JOURNAL [TO] file [DX x] [DY y]\n");
  681.          printf("                [SMOOTHX x] [SMOOTHY y] [SMOOTH mask]");
  682.          printf(               " [TRIGGER mask]\n");
  683.          break;
  684.  
  685.       case WRITE_JOURNAL:
  686.          RecordJournal();
  687.          break;
  688.    }
  689.    DoExit(NULL);
  690. }
  691.